package com.my.yyr.security;

import com.alibaba.fastjson.JSON;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

@EnableWebSecurity
@MapperScan("com.my.yyr.security")//扫描安全dao
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private SecurityDao securityDao;

    public SecurityConfig(SecurityDao securityDao){
        this.securityDao = securityDao;
    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean //定义获取当前用户信息的业务对象
    public UserDetailsService userDetailsService(){
        System.out.println("============================================");
        System.out.println(passwordEncoder().encode("123"));
        System.out.println("============================================");

        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {

                User user = securityDao.findUserByUserId(userId);

                List<String> moduleIdList = securityDao.findModuleIdListByUserId(userId);

                String authority_pattern = "ROLE_{0}";
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                for(String moduleId : moduleIdList){
                    //Spring Security中的用户拥有的权限(角色)命名规则为“ROLE_xxxx”
                    SimpleGrantedAuthority authority =
                            new SimpleGrantedAuthority(MessageFormat.format(authority_pattern,moduleId));
                    authorities.add(authority);
                }

                return new CurrUser(
                        user.getU_id(),
                        user.getU_name(),
                        user.getU_pwd(),//目前spring security要求此密码必须加密过
                        user.getU_status().equals(DataStatusEnum.已启用.getCode()),
                        authorities
                );
            }
        };

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        /*
            授权认证配置
        */

        //通过查询数据库，将模块地址和相应权限编号对应起来，访问某个模块必须要有相应的权限；
        String urlPattern = "{0}/**";
        List<Module> moduleList = securityDao.findModuleList();

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlRegistry = http.authorizeRequests();

        for(Module module : moduleList){
            urlRegistry.antMatchers(MessageFormat.format(urlPattern,module.getM_url())).hasRole(module.getM_id().toString());
        }
        urlRegistry.anyRequest().authenticated();//其余请求必须认证

        /*
            登录配置
         */
        http
                .formLogin()
                .loginProcessingUrl("/security/login") //这里仅仅是为spring security提供的登录验证处理对应一个url(仅支持post请求form-data)
                .usernameParameter("u_id")
                .passwordParameter("u_pwd")
                .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.print(JSON.toJSONString(Result.success("登录成功")));
                    out.flush();
                })
                .failureHandler((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    //out.print(JSON.toJSONString(Result.fail(e.getMessage())));
                    out.print(JSON.toJSONString(Result.fail("不要意思，您登录失败了！")));
                    out.flush();
                })
                .permitAll()//允许所有请求访问登录相关接口

                .and()
            /*
               退出配置
            */
                .logout()
                .logoutUrl("/security/logout")
                .logoutSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.print(JSON.toJSONString(Result.success("您已成功退出系统！")));
                    out.flush();
                })

                .and()

            /*
            认证、授权异常处理
            */
                .exceptionHandling()
                //处理认证异常（未登录，而访问需要登录访问的资源）
                .authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.print(JSON.toJSONString(Result.unLogined()));
                    out.flush();
                })
                //处理授权异常（已经登录，但是访问了没有权限的资源）
                .accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.print(JSON.toJSONString(Result.unAuthorized()));
                    out.flush();
                })

                .and()
             /*
                配置允许跨域
            */
                .cors()//没有进行任何配置，即自动采用spring mvc的跨域配置

                .and()
            /*
                配置禁用csrf
             */
                .csrf().disable();


    }
}
